﻿#include "stdafx.h"
#include "SoundTools.h"

_NAMESPACE_BEGIN

SoundTool::SoundTool()
{
	if(InitialFMOD())
	{
		SAVE_LOG("FMOD声音引擎初始化成功");
	}
	else
	{
		SAVE_LOG("FMOD声音引擎初始化失败");
	}
}

SoundTool::~SoundTool()
{
	FMOD_RESULT      result;

	// 释放所有音效
	for(std::vector<SoundEffect*>::iterator i = soundList.begin(); i != 

		soundList.end(); ++i)
	{
		result = (*i)->sound->release();
		ERRCHECK(result);
	}

	result = system->close();
	ERRCHECK(result);
	result = system->release();
	ERRCHECK(result);

	SAVE_LOG("FMOD声音引擎释放成功");
}

bool SoundTool::InitialFMOD()
{
	FMOD_RESULT      result;

	result = FMOD::System_Create(&system);
	ERRCHECK(result);

	result = system->getVersion(&version);
	ERRCHECK(result);

	if (version < FMOD_VERSION)
	{
		printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
		return false;
	}

	result = system->getNumDrivers(&numdrivers);
	ERRCHECK(result);

	if (numdrivers == 0)
	{
		result = system->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
		ERRCHECK(result);
	}
	else
	{
		result = system->getDriverCaps(0, &caps, 0, 0, &speakermode);
		ERRCHECK(result);

		result = system->setSpeakerMode(speakermode);       /* Set the user selected speaker mode. */
		ERRCHECK(result);

		if (caps & FMOD_CAPS_HARDWARE_EMULATED)             /* The user has the 'Acceleration' slider set to off!  This is really bad for latency!. */
		{                                                   /* You might want to warn the user about this. */
			result = system->setDSPBufferSize(1024, 10);
			ERRCHECK(result);
		}

		result = system->getDriverInfo(0, name, 256, 0);
		ERRCHECK(result);

		if (strstr(name, "SigmaTel"))   /* Sigmatel sound devices crackle for some reason if the format is PCM 16bit.  PCM floating point output seems to solve it. */
		{
			result = system->setSoftwareFormat(48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, FMOD_DSP_RESAMPLER_LINEAR);
			ERRCHECK(result);
		}
	}

	result = system->init(100, FMOD_INIT_NORMAL|FMOD_INIT_3D_RIGHTHANDED, 0);	// 强调右手坐标系

	if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)         /* Ok, the speaker mode selected isn't supported by this soundcard.  Switch it back to stereo... */
	{
		result = system->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
		ERRCHECK(result);

		result = system->init(100, FMOD_INIT_NORMAL|FMOD_INIT_3D_RIGHTHANDED, 0);/* ... and re-init. */
		ERRCHECK(result);
	}

	INTERFACE_UPDATETIME = 50;      // 听者 50ms更新
	DISTANCEFACTOR = 1.0f;          // 单位是米

	// 设距离
	result = system->set3DSettings(1.0, DISTANCEFACTOR, 1.0f);
	ERRCHECK(result);

	listenerpos.x = 0;
	listenerpos.y = 0;
	listenerpos.z = -1.0f * DISTANCEFACTOR;
	
	// 时钟计时开始
	clock.Start();
	return true;
}

void SoundTool::Update(/*CONST_REF(Vector3) lisPos*/)
{
	// 更新太快声音播放会严重失真
	if (clock.GetElapse() < 0.05)
	{
		return;
	}
	else 
	{
		clock.Start();
	}

	// 播放声音
	PlaySound();

	//////////////////////////////////////////////////////////////////////////
	// 听者更新

	static float t = 0;
	static FMOD_VECTOR lastpos = { 0.0f, 0.0f, 0.0f };
	FMOD_VECTOR forward        = { 0.0f, 0.0f, -1.0f };
	FMOD_VECTOR up             = { 0.0f, 1.0f, 0.0f };
	FMOD_VECTOR vel;

	vel.x = (listenerpos.x - lastpos.x) * (1000 / INTERFACE_UPDATETIME);
	vel.y = (listenerpos.y - lastpos.y) * (1000 / INTERFACE_UPDATETIME);
	vel.z = (listenerpos.z - lastpos.z) * (1000 / INTERFACE_UPDATETIME);

	lastpos = listenerpos;

	FMOD_RESULT result;
	result = system->set3DListenerAttributes(0, &listenerpos, &vel, &forward, &up);
	ERRCHECK(result);

	// 引擎更新
	system->update();
}

SoundEffect* SoundTool::AddNewSound( CONST_REF(string) fileName )
{
	SoundEffect* se = new SoundEffect(system);
	FMOD_RESULT result;
	result = system->createStream(fileName.c_str(), FMOD_HARDWARE, 0, &se->sound);
	ERRCHECK(result);
	result = se->sound->set3DMinMaxDistance(0.5f * DISTANCEFACTOR, 5000.0f * DISTANCEFACTOR);
	ERRCHECK(result);
	result = se->sound->setMode(FMOD_LOOP_NORMAL);
	ERRCHECK(result);

// 	for (std::vector<SoundEffect*>::iterator i=soundList.begin(); i!= soundList.end(); ++i)
// 	{
// 
// 	}
	
	soundList.push_back(se);
	return se;
}

void SoundTool::PlaySound()
{
	for(std::vector<SoundEffect*>::iterator i = soundList.begin(); i != soundList.end(); ++i)
	{
		(*i)->Play();
	}
}

void SoundTool::SetListenerPos( CONST_REF(Vector3) lisPos )
{
	listenerpos.x = lisPos.x;
	listenerpos.y = lisPos.y;
	listenerpos.z = lisPos.z;
}
//////////////////////////////////////////////////////////////////////////

SoundEffect::SoundEffect( FMOD::System* sys )
{
	sound = NULL;
	isPlaying = false;
	speed = 0;
	system = sys;
	channel = NULL;
	assert(system);
}

void SoundEffect::Play()
{
	// 播放
	if (isPlaying)
	{
		if (speed ==0)		// 由速度决定播放
		{
			//bool playing = false;
			//if(channel)
			//{
			//	channel->isPlaying(&playing);
			//}
			//if (!playing)
			{
				FMOD_VECTOR position = {pos.x,pos.y,pos.z};
				FMOD_VECTOR vel = {  0.0f, 0.0f, 0.0f };
				system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
				channel->setMode(FMOD_LOOP_NORMAL);
				sound->setMode(FMOD_LOOP_NORMAL);
				channel->set3DAttributes(&position, &vel);
				channel->setPaused(false);

				//44100-默认频率
				channel->setFrequency(0);
				channel->setVolume(0.2f);
			}
		}
		else if(speed == -1)	// 正常播放
		{
			bool playing = false;
			if(channel)
			{
				channel->isPlaying(&playing);
			}
			if (!playing && isPlaying)
			{
				FMOD_VECTOR position = {pos.x,pos.y,pos.z};
				FMOD_VECTOR vel = {  0.0f, 0.0f, 0.0f };
				system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
				channel->set3DAttributes(&position, &vel);
				channel->setPaused(false);

				//44100-默认频率
				channel->setFrequency(44100);
				channel->setVolume(1);
			}
		}
		else					// 由速度决定播放
		{
			float fq = 250*speed /*+ 5000*/;	// 根据速度改频率
			channel->setFrequency(fq);
		}
	}

	// 停止
	else
	{
		system->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
		channel->stop();
	}
}

_NAMESPACE_END